'use client';

import type { IDifference, IPostEditInfo, IPostNewInfo } from '@/interfaces';
import type { TEditorDynamicTranslatedField } from '@/app/[locale]/posts/[id]/edit/editor/editor';
import type { TFileManagerTranslatedField } from '@/app/[locale]/file/file-manager';
import dynamic from 'next/dynamic';
import EditorSuspense from '@/app/[locale]/posts/[id]/edit/editor/load';
import {
  handleEditorBeforeunload,
  handleEditorSave,
  handleEditorStatusChanges,
} from '@/lib/editor/handle';
import { getEditorContentHtml, setEditorContentHtml } from '@/lib/editor';
import type { ChangeEvent } from 'react';
import { useEffect, useRef, useState } from 'react';
import useToast from '@/hooks/useToast';
import sanitizeHtml from 'sanitize-html';
import { useMutation } from '@tanstack/react-query';
import { updatePostEditInfo, updatePostNewInfo } from '@/services/api';
import classNames from 'classnames';
import diff from 'microdiff';
import { getDiffData } from '@/lib/tool';
import Script from 'next/script';

declare let wx: any;

const DynamicEditor: any = dynamic(
  () => import('../../posts/[id]/edit/editor/editor'),
  {
    loading: () => <EditorSuspense />,
    ssr: false,
  },
);

export default function WebView({
  options,
  source,
  editorTranslatedFields,
  fileManagerTranslatedFields,
  env,
}: {
  options: { sectionId?: string; token: string };
  source: IPostEditInfo | IPostNewInfo;
  editorTranslatedFields?: Record<TEditorDynamicTranslatedField, any>;
  fileManagerTranslatedFields?: Record<TFileManagerTranslatedField, any>;
  env: {
    appIcpNum: string;
    appIcpLink: string;
    appName: string;
    year: string;
  };
}) {
  const isEdit = 'basic' in source;
  const id = isEdit ? source.basic.id : undefined;
  const sections = source.sections;
  const editorRef = useRef<any>();
  const [isActive, setIsActive] = useState(false);
  const [isSaving, setIsSaving] = useState(false);
  const isDirty = useRef(false);
  const [isReady, setIsReady] = useState(false);
  const isLoadingSave = useRef(false);
  const { show } = useToast();
  const [form, setForm] = useState({
    name: isEdit ? source.basic.name : '',
    content: '',
    sectionId: isEdit
      ? source.section.id + ''
      : options.sectionId ?? sections.length > 0
      ? sections[0].id + ''
      : '',
    overview: isEdit ? source.basic.overview ?? '' : '',
    images: isEdit ? source.basic.images : [],
  });
  const [contract, setContract] = useState(false);
  const [differenceData, setDifferenceData] = useState<IDifference[]>([]);
  const wxRef = useRef<any>();
  const [isMp, setIsMp] = useState(false);
  const [isLoading, setIsLoading] = useState(true);

  useEffect(() => {
    let obj: {};
    if (isEdit) {
      obj = {
        name: source.basic.name,
        sectionId: source.section.id + '',
        overview: source.basic.overview ?? '',
        images: source.basic.images,
      };
    } else {
      obj = {
        overview: '',
        images: [],
      };
    }

    setDifferenceData(diff(obj, form));
  }, [form, isEdit]);

  const updatePostEditInfoMutation = useMutation(updatePostEditInfo);
  const updatePostNewInfoMutation = useMutation(updatePostNewInfo);

  async function onClickSave() {
    const editor = editorRef.current;
    if (!editor) {
      return;
    }

    await handleEditorSave({
      editor,
      isDirty,
      setIsActive,
      setIsSaving,
      callback: onClickSaveCore,
    });
  }

  async function onClickSaveCore({
    data,
    reset,
  }: {
    data: string;
    reset: () => void;
  }) {
    const current = wxRef.current;
    if (!current) {
      show({
        type: 'DANGER',
        message: '数据不存在，请刷新重试',
      });
      return;
    }

    let isOk = false;
    try {
      isLoadingSave.current = true;

      isOk = checkForm(data);

      let url: string;
      const body = handleBody(data);
      const token = options.token;

      if (isEdit) {
        await updatePostEditInfoMutation.mutateAsync({
          id: id,
          token,
          data: body,
        });

        url = `/pages/details/post/index?id=${id}`;
      } else {
        const { headers } = (await updatePostNewInfoMutation.mutateAsync({
          token,
          data: body,
        })) as Response;

        const locationValue = headers.get('location');
        if (locationValue) {
          const split = locationValue.split('/');
          if (split.length > 0) {
            url = `/pages/details/post/index?id=${split[split.length - 1]}`;
          }
        }
      }

      show({
        type: 'SUCCESS',
        message: '保存完成',
      });

      setTimeout(() => {
        show({
          type: 'SUCCESS',
          message: '即将返回',
        });
      }, 1000);

      setTimeout(() => {
        if (url) {
          current.miniProgram.navigateTo({ url });
        } else {
          wx.miniProgram.navigateBack();
        }
      }, 2000);
    } catch (e) {
      if (isEdit) {
        updatePostEditInfoMutation.reset();
      } else {
        updatePostNewInfoMutation.reset();
      }

      show({
        type: 'DANGER',
        message: e,
      });
    } finally {
      isLoadingSave.current = false;
      reset();

      if (!isOk) {
        setIsActive((prevState) => !prevState);
      }
    }
  }

  function handleBody(data: string) {
    const body = getDiffData(differenceData);
    body.content = getContent(data);
    body.images = getImages(body.content);
    if (body.overview) {
      body.overview = sanitizeHtml(body.overview);
    }

    return body;
  }

  function getContent(data?: any) {
    return getEditorContentHtml(editorRef.current, data);
  }

  function getImages(content: string) {
    if (!content) {
      return [];
    }

    return (content.match(/<img.*?>/g) || [])
      .filter((item) => item.includes('width=') && item.includes('height='))
      .map((item) => {
        const exec = /src="([^"\s]+)"/g.exec(item);
        return exec ? exec[1].trim() : null;
      })
      .filter((item) => item)
      .filter((value, index, array) => array.indexOf(value) === index);
  }

  function checkForm(data: any) {
    const { name, sectionId } = form;
    if (!name.trim()) {
      throw '标题不能为空';
    }

    if (!sectionId) {
      throw '未选择内容';
    }

    const content = getContent(data);
    if (!content.trim()) {
      throw '内容不能为空';
    }

    return true;
  }

  function onChange(
    e: ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>,
  ) {
    const name = e.target.name;
    const value = e.target.value;
    setForm({ ...form, [name]: value });
  }

  function onReady({ editor }: { editor: any }) {
    handleEditorStatusChanges({
      editor,
      isDirty,
      setIsActive,
      setIsSaving,
    });
    handleEditorBeforeunload({ editor });
    setEditorContentHtml(editor, form.content);
    editorRef.current = editor;
    setIsReady(true);
  }

  function onClickChevronContract() {
    setContract(!contract);
  }

  function onWeixinJsSdk() {
    wxRef.current = wx;
    wx.miniProgram.getEnv((res: { miniprogram: boolean }) => {
      setIsMp(res.miniprogram);
    });
    setIsLoading(false);
  }

  return (
    <>
      {isMp ? (
        <div
          className="p-2 d-flex flex-column gap-2"
          style={{ background: '#2e2e2e' }}
        >
          <div className="row my-3 align-items-center">
            <div className="col">
              <div
                onClick={onClickChevronContract}
                className="link-light cursor-pointer"
              >
                {contract ? (
                  <i className="bi bi-chevron-expand fs-4"></i>
                ) : (
                  <i className="bi bi-chevron-contract fs-4"></i>
                )}
              </div>
            </div>
            <div className="col-6">
              <button
                disabled={!isActive || isSaving || isLoadingSave.current}
                onClick={onClickSave}
                type="button"
                className="btn btn-success w-100"
              >
                {isSaving || isLoadingSave.current ? (
                  <span
                    className="spinner-border spinner-border-sm me-2"
                    aria-hidden="true"
                  ></span>
                ) : (
                  <i className="bi bi-save me-2"></i>
                )}
                保存返回
              </button>
            </div>
          </div>

          {contract && (
            <div
              className={classNames(
                'card border-0 animate__animated animate__faster animate__backInLeft',
              )}
            >
              <div className="card-body d-flex flex-column gap-2 px-2">
                <input
                  type="text"
                  name="name"
                  value={form.name}
                  className="form-control"
                  onChange={onChange}
                  placeholder="标题"
                />

                <select
                  name="sectionId"
                  className="form-select"
                  value={form.sectionId}
                  onChange={onChange}
                  placeholder="内容"
                >
                  {sections.map((item) => {
                    return (
                      <option key={item.id} value={item.id}>
                        {item.name}
                      </option>
                    );
                  })}
                </select>

                <textarea
                  name="overview"
                  value={form.overview}
                  onChange={onChange}
                  className="form-control"
                  rows={1}
                  placeholder="概述"
                />
              </div>
            </div>
          )}

          <div className="card border-0">
            <div className="card-body px-2">
              <DynamicEditor
                id={id}
                onReadyCallback={onReady}
                translatedFields={editorTranslatedFields}
                fileManagerTranslatedFields={fileManagerTranslatedFields}
              />
            </div>
          </div>

          {env.appIcpNum && env.appIcpLink && (
            <footer className="py-4 small">
              <div className="d-flex flex-column align-items-center justify-content-center text-secondary">
                <p className="mb-2">
                  &copy; {env.year}Y &nbsp;{env.appName}
                </p>
                <a
                  href="#"
                  className="text-decoration-none text-secondary"
                  rel="noopener noreferrer"
                  target="_blank"
                >
                  {env.appIcpNum}
                </a>
              </div>
            </footer>
          )}
        </div>
      ) : (
        <>
          {isLoading ? (
            <div className="fixed-top px-0">
              <div className="vh-100 yw-mp-bg bg-opacity-10 animate__animated animate__faster animate__zoomIn">
                <div className="position-absolute top-50 start-50 translate-middle d-flex flex-column align-items-center justify-content-center gap-2">
                  <div className="rounded-circle circle-48 bg-white d-flex align-items-center justify-content-center shadow-lg">
                    <div
                      className="spinner-border text-secondary"
                      role="status"
                    ></div>
                  </div>
                  <div className="d-flex justify-content-center align-items-center fs-5 text-white bg-black px-4 rounded bg-opacity-25">
                    Loading...
                  </div>
                </div>
              </div>
            </div>
          ) : (
            <div className="px-2 py-3">
              <div
                className="card bg-transparent animate__animated animate__faster animate__pulse border-2"
                style={{ borderColor: '#444444' }}
              >
                <div className="card-body">
                  <div className="ratio ratio-16x9">
                    <div className="text-secondary vstack gap-2 text-center align-items-center justify-content-center">
                      <div>编辑器不存在</div>
                    </div>
                  </div>
                </div>
              </div>
            </div>
          )}
        </>
      )}

      <Script onReady={onWeixinJsSdk} src="/lib/weixin-jssdk.min.js" />
    </>
  );
}
